Completed
Push — master ( bebc18...c39f44 )
by Sander
01:09
created

background.js ➔ getCredentials   B

Complexity

Conditions 1
Paths 2

Size

Total Lines 32

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
c 1
b 0
f 0
nc 2
nop 0
dl 0
loc 32
rs 8.8571
1
/* global API */
2
3
var background = (function () {
4
    var storage = new API.Storage();
5
    var _self = this;
6
    var _window = {};
7
8
9
10
    API.runtime.onConnect.addListener(function (port) {
11
12
        port.onMessage.addListener(function (msg) {
13
            if (msg === 'credential_amount') {
14
                port.postMessage('credential_amount:' + local_credentials.length);
15
            }
16
17
        });
18
19
    });
20
21
    var master_password = null;
22
23
    function getMasterPasswordSet() {
24
        return (master_password !== null);
25
    }
26
27
    _self.getMasterPasswordSet = getMasterPasswordSet;
28
29
    function setMasterPassword(opts) {
30
        master_password = opts.password;
31
        if (opts.hasOwnProperty('savePassword') && opts.savePassword === true) {
32
            // Save the password in plain text on user request.
33
            // No secure local storage is available :/
34
            storage.set('master_password', opts.password);
35
        } else {
36
            storage.set('master_password', null);
37
        }
38
39
        if (opts.password) {
40
            getSettings();
41
        } else {
42
            displayLogoutIcons();
43
        }
44
45
    }
46
47
    _self.setMasterPassword = setMasterPassword;
48
49
50
    var testMasterPasswordAgainst;
51
52
    function isMasterPasswordValid(password) {
53
        //return true;
54
        try {
55
            PAPI.decryptString(testMasterPasswordAgainst, password);
56
            return true;
57
        } catch (e) {
58
            return false;
59
        }
60
    }
61
62
    _self.isMasterPasswordValid = isMasterPasswordValid;
63
64
65
    var local_credentials = [];
66
    var local_vault = [];
67
    var encryptedFieldSettings = ['default_vault', 'nextcloud_host', 'nextcloud_username', 'nextcloud_password', 'vault_password'];
68
    _self.settings = {};
69
    _self.ticker = null;
70
    _self.running = false;
71
    function getSettings() {
72
73
        storage.get('settings').then(function (_settings) {
74
75
            if (!_settings || !_settings.hasOwnProperty('nextcloud_host')) {
76
                return;
77
            }
78
79
            if (!master_password && _settings.hasOwnProperty('nextcloud_username') && _settings.hasOwnProperty('vault_password')) {
80
                _self.settings.isInstalled = 1;
81
                testMasterPasswordAgainst = _settings.nextcloud_username;
82
                return;
83
            }
84
85
            for (var i = 0; i < encryptedFieldSettings.length; i++) {
86
                var field = encryptedFieldSettings[i];
87
                _settings[field] = JSON.parse(PAPI.decryptString(_settings[field], master_password));
88
            }
89
90
91
            _self.settings = _settings;
92
93
            if(!_self.settings.hasOwnProperty('ignored_sites')){
94
                _self.settings.ignored_sites = [];
95
            }
96
97
            if (!_self.settings.hasOwnProperty('disable_browser_autofill')) {
98
                _self.settings.disable_browser_autofill = true;
99
            }
100
101
102
            PAPI.host = _settings.nextcloud_host;
103
            PAPI.username = _settings.nextcloud_username;
104
            PAPI.password = _settings.nextcloud_password;
105
            if (!_settings.vault_password) {
106
                return;
107
            }
108
            if (PAPI.credentialsSet()) {
109
                getCredentials();
110
                if (_self.running) {
111
                    clearInterval(_self.ticker);
112
                }
113
114
                _self.running = true;
115
                _self.ticker = setInterval(function () {
116
                    getCredentials();
117
                }, _self.settings.refreshTime * 1000);
118
            } else {
119
                console.log('Login details are missing!');
0 ignored issues
show
Debugging Code introduced by
console.log looks like debug code. Are you sure you do not want to remove it?
Loading history...
120
            }
121
        });
122
    }
123
124
    _self.getSettings = getSettings;
125
126
    function getRuntimeSettings() {
127
        return _self.settings;
128
    }
129
130
    _self.getRuntimeSettings = getRuntimeSettings;
131
132
    function getSetting(name) {
133
        return _self.settings[name];
134
    }
135
136
    _self.getSetting = getSetting;
137
138
    function saveSettings(settings, cb) {
0 ignored issues
show
Unused Code introduced by
The parameter cb is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
139
        for (var i = 0; i < encryptedFieldSettings.length; i++) {
140
            var field = encryptedFieldSettings[i];
141
            settings[field] = PAPI.encryptString(JSON.stringify(settings[field]), master_password);
142
        }
143
        PAPI.host = settings.nextcloud_host;
144
        PAPI.username = settings.nextcloud_username;
145
        PAPI.password = settings.nextcloud_password;
146
147
        if(!settings.hasOwnProperty('ignored_sites')){
148
            settings.ignored_sites = [];
149
        }
150
151
        if (!settings.hasOwnProperty('disable_browser_autofill')) {
152
            settings.disable_browser_autofill = true;
153
        }
154
155
        //window.settings contains the run-time settings
156
        _self.settings = settings;
157
158
159
160
        storage.set('settings', settings).then(function () {
161
            getSettings();
162
        });
163
164
    }
165
166
    _self.saveSettings = saveSettings;
167
168
169
    function getCredentials() {
170
        //console.log('Loading vault with the following settings: ', settings);
171
        var tmpList = [];
172
        PAPI.getVault(_self.settings.default_vault.guid, function (vault) {
173
            if (vault.hasOwnProperty('error')) {
174
                return;
175
            }
176
            var _credentials = vault.credentials;
177
            for (var i = 0; i < _credentials.length; i++) {
178
                var key = _self.settings.vault_password;
179
                var credential = _credentials[i];
180
                if (credential.hidden === 1) {
181
                    continue;
182
                }
183
                var usedKey = key;
184
                //Shared credentials are not implemented yet
185
                if (credential.hasOwnProperty('shared_key') && credential.shared_key) {
186
                    usedKey = PAPI.decryptString(credential.shared_key, key);
187
188
                }
189
                credential = PAPI.decryptCredential(credential, usedKey);
190
                if (credential.delete_time === 0) {
191
                    tmpList.push(credential);
192
                }
193
194
            }
195
            delete vault.credentials;
196
            local_vault = vault;
197
            local_credentials = tmpList;
198
            updateTabsIcon();
199
        });
200
    }
201
202
    _self.getCredentials = getCredentials;
203
204
    function getCredentialsByUrl(_url, sender) {
0 ignored issues
show
Unused Code introduced by
The parameter sender is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
205
        if (!master_password) {
206
            return [];
207
        }
208
        if (!_url || _url === '') {
209
            return [];
210
        }
211
        var url = processURL(_url, _self.settings.ignoreProtocol, _self.settings.ignoreSubdomain, _self.settings.ignorePath, _self.settings.ignorePort);
212
        var found_list = [];
213
        for (var i = 0; i < local_credentials.length; i++) {
214
            if (local_credentials[i].url && local_credentials[i].username && local_credentials[i].password) {
215
                if (local_credentials[i].url.indexOf(url) !== -1) {
216
                    found_list.push(local_credentials[i]);
217
                }
218
            }
219
        }
220
        return found_list;
221
    }
222
223
    _self.getCredentialsByUrl = getCredentialsByUrl;
224
225
226
    function getCredentialByGuid(guid) {
227
        for (var i = 0; i < local_credentials.length; i++) {
228
            var credential = local_credentials[i];
229
            if (credential.guid === guid) {
230
                return credential;
231
            }
232
        }
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
233
    }
234
235
    _self.getCredentialByGuid = getCredentialByGuid;
236
237
    function getCredentialForHTTPAuth(req) {
238
        return getCredentialsByUrl(req.url)[0];
239
    }
240
241
    _window.getCredentialForHTTPAuth = getCredentialForHTTPAuth;
242
243
    var mined_data = [];
244
245
    function minedForm(data, sender) {
246
        var url = sender.url;
247
        var existingLogins = getCredentialsByUrl(sender.url);
248
        var title = API.i18n.getMessage('detected_new_login') + ':';
249
        var minedMatchingID = null;
250
        for (var j = 0; j < existingLogins.length; j++) {
251
            var login = existingLogins[j];
252
            if (login.username === data.username) {
253
                if (login.password !== data.password) {
254
                    minedMatchingID = login.guid;
255
                    title = API.i18n.getMessage('detected_changed_login') + ':';
256
                }
257
                else {
258
                    //console.log('No changes detected');
259
                    delete mined_data[sender.tab.id];
260
                    return;
261
                }
262
            }
263
        }
264
        mined_data[sender.tab.id] = {
265
            title: title,
266
            url: url,
267
            username: data.username,
268
            password: data.password,
269
            label: sender.title,
270
            guid: minedMatchingID
271
        };
272
273
        //console.log('Done mining, ', mined_data, sender.tab.id);
274
    }
275
276
    _self.minedForm = minedForm;
277
278
    function getMinedData(args, sender) {
279
        //console.log('Fecthing  mined data for tab id', sender.tab.id)
280
        var senderUrl = sender.tab.url;
281
        var site = processURL(senderUrl, _self.settings.ignoreProtocol, _self.settings.ignoreSubdomain, _self.settings.ignorePath, _self.settings.ignorePort);
282
        var matches = _self.settings.ignored_sites.filter(function (item) {
283
            return typeof item === 'string' && site.indexOf(item) > -1;
284
        });
285
286
        if(matches.length !== 0){
287
            return null;
288
        }
289
        return mined_data[sender.tab.id];
290
    }
291
292
    _self.getMinedData = getMinedData;
293
294
    function clearMined(args, sender) {
295
        delete mined_data[sender.tab.id];
296
    }
297
298
    _self.clearMined = clearMined;
299
300
    function saveMinedCallback(args) {
301
        createIconForTab(args.sender.tab);
302
        API.tabs.query({active: true, currentWindow: true}).then(function (tabs) {
303
            API.tabs.sendMessage(args.sender.tab.id, {method: "minedLoginSaved", args: args}).then(function (response) {
0 ignored issues
show
Unused Code introduced by
The parameter response is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
304
            });
305
        });
306
    }
307
308
    function ignoreSite(_url) {
309
        if (!_self.settings.hasOwnProperty('ignored_sites')) {
310
            _self.settings.ignored_sites = [];
311
        }
312
        var site = processURL(_url, _self.settings.ignoreProtocol, _self.settings.ignoreSubdomain, _self.settings.ignorePath, _self.settings.ignorePort);
313
        if (_self.settings.ignored_sites.indexOf(site) === -1) {
314
            _self.settings.ignored_sites.push(site);
315
            saveSettings(_self.settings);
316
        }
317
    }
318
319
    _self.ignoreSite = ignoreSite;
320
321
    function passToParent(args, sender) {
322
        API.tabs.sendMessage(sender.tab.id, {method: args.injectMethod, args: args.args}).then(function (response) {
0 ignored issues
show
Unused Code introduced by
The parameter response is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
323
        });
324
    }
325
326
    _self.passToParent = passToParent;
327
328
    function getActiveTab(opt) {
329
        API.tabs.query({active: true, currentWindow: true}).then(function (tabs) {
330
            var tab = tabs[0];
331
            API.tabs.sendMessage(tab.id, {method: opt.returnFn, args: tab}).then(function (response) {
0 ignored issues
show
Unused Code introduced by
The parameter response is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
332
            });
333
        });
334
    }
335
336
    _self.getActiveTab = getActiveTab;
337
338
    function updateCredentialUrlDoorhanger(login) {
339
        API.tabs.query({active: true, currentWindow: true}).then(function (tabs) {
340
            var tab = tabs[0];
341
            var data = login;
342
            data.url = tab.url;
343
            data.title = API.i18n.getMessage('detected_changed_url') + ':';
344
            API.tabs.sendMessage(tab.id, {
345
                method: 'showUrlUpdateDoorhanger',
346
                args: {data: data}
347
            });
348
        });
349
    }
350
351
    _self.updateCredentialUrlDoorhanger = updateCredentialUrlDoorhanger;
352
353
    function updateCredentialUrl(data, sender) {
354
        mined_data[sender.tab.id] = data;
355
        saveMined({}, sender);
356
357
    }
358
359
    _self.updateCredentialUrl = updateCredentialUrl;
360
361
    function saveMined(args, sender) {
362
        var data = mined_data[sender.tab.id];
363
        var credential,
364
            credential_index;
365
366
        if (data.guid === null) {
367
            credential = PAPI.newCredential();
368
        } else {
369
            for (var i = 0; i < local_credentials.length; i++) {
370
                if (local_credentials[i].guid === data.guid) {
371
                    credential = local_credentials[i];
372
                    credential_index = i;
373
                    break;
374
                }
375
            }
376
        }
377
        credential.username = data.username;
0 ignored issues
show
Bug introduced by
The variable credential seems to not be initialized for all possible execution paths.
Loading history...
378
        credential.password = data.password;
379
        credential.url = sender.tab.url;
380
        if (credential.guid !== null) {
381
            PAPI.updateCredential(credential, _self.settings.vault_password, function (updatedCredential) {
382
                if (credential_index) {
383
                    local_credentials[credential_index] = updatedCredential;
384
                }
385
                saveMinedCallback({credential: credential, updated: true, sender: sender});
0 ignored issues
show
Bug introduced by
The variable credential seems to not be initialized for all possible execution paths.
Loading history...
386
                delete mined_data[sender.tab.id];
387
            });
388
        } else {
389
            credential.label = sender.tab.title;
390
            credential.vault_id = local_vault.vault_id;
391
            PAPI.createCredential(credential, _self.settings.vault_password, function (createdCredential) {
392
                saveMinedCallback({credential: credential, updated: false, sender: sender});
0 ignored issues
show
Bug introduced by
The variable credential seems to not be initialized for all possible execution paths.
Loading history...
393
                local_credentials.push(createdCredential);
394
                delete mined_data[sender.tab.id];
395
            });
396
        }
397
    }
398
399
    _self.saveMined = saveMined;
400
401
    function searchCredential(searchText) {
402
        var searchFields = ['label', 'username', 'email', 'url', 'description'];
403
        var results = [];
404
        for (var i = 0; i < local_credentials.length; i++) {
405
            var credential = local_credentials[i];
406
            for (var f = 0; f < searchFields.length; f++) {
407
                var field = searchFields[f];
408
                if (credential[field] && credential[field].indexOf(searchText) !== -1) {
409
                    results.push(credential);
410
                    break;
411
                }
412
            }
413
        }
414
        return results;
415
    }
416
417
    _self.searchCredential = searchCredential;
418
419
420
    function injectCreateCredential(args, sender) {
421
        var credential = PAPI.newCredential();
422
        credential.label = args.label;
423
        credential.username = args.username;
424
        credential.password = args.password;
425
        credential.vault_id = local_vault.vault_id;
426
        credential.url = sender.tab.url;
427
        PAPI.createCredential(credential, _self.settings.vault_password, function (createdCredential) {
428
            saveMinedCallback({credential: credential, updated: false, sender: sender, selfAdded: true});
429
            local_credentials.push(createdCredential);
430
431
        });
432
    }
433
434
    self.injectCreateCredential = injectCreateCredential;
0 ignored issues
show
Bug introduced by
The variable self seems to be never declared. If this is a global, consider adding a /** global: self */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
435
436
    function isVaultKeySet() {
437
        return (_self.settings.vault_password !== null);
438
    }
439
440
    _self.isVaultKeySet = isVaultKeySet;
441
442
    function isAutoFillEnabled() {
443
        if (!_self.settings.hasOwnProperty('disableAutoFill')) {
444
            return true;
445
        }
446
        return (_self.settings.disableAutoFill === false);
447
    }
448
449
    _self.isAutoFillEnabled = isAutoFillEnabled;
450
451
    var doorhangerData = null;
452
    function setDoorhangerData(data) {
453
        doorhangerData = data;
454
    }
455
    _self.setDoorhangerData = setDoorhangerData;
456
457
    function getDoorhangerData() {
458
        return doorhangerData;
459
    }
460
    _self.getDoorhangerData = getDoorhangerData;
461
462
    API.runtime.onMessage.addListener(function (msg, sender, sendResponse) {
463
464
        if (!msg || !msg.hasOwnProperty('method')) {
465
            return;
466
        }
467
        var result = false;
468
        if (_self[msg.method]) {
469
            result = _self[msg.method](msg.args, sender);
470
        } else {
471
            console.warn('[NOT FOUND] Method call', msg.method, 'args: ', msg.args);
472
        }
473
474
        sendResponse(result);
475
    });
476
477
    var defaultColor = '#0082c9';
478
479
    function createIconForTab(tab) {
480
        if (!master_password) {
481
            return;
482
        }
483
        var tabUrl = tab.url;
484
        var logins = getCredentialsByUrl(tabUrl);
485
        if (tab.active) {
486
            window.contextMenu.setContextItems(logins);
487
        }
488
        var credentialAmount = logins.length;
489
        API.browserAction.setBadgeText({
490
            text: credentialAmount.toString(),
491
            tabId: tab.id
492
        });
493
        API.browserAction.setBadgeBackgroundColor({
494
            color: defaultColor,
495
            tabId: tab.id
496
        });
497
498
        var plural = (credentialAmount === 1) ? API.i18n.getMessage('credential') : API.i18n.getMessage('credentials');
499
        API.browserAction.setTitle({
500
            title: API.i18n.getMessage('browser_action_title_login', [credentialAmount.toString(), plural.toString()]),
501
            tabId: tab.id
502
        });
503
    }
504
505
    function displayLogoutIcons() {
506
        if (_self.settings) {
507
            API.tabs.query({}).then(function (tabs) {
508
                for (var t = 0; t < tabs.length; t++) {
509
                    var tab = tabs[t];
510
                    API.browserAction.setBadgeText({
511
                        text: '🔑',
512
                        tabId: tab.id
513
                    });
514
                    API.browserAction.setBadgeBackgroundColor({
515
                        color: '#ff0000',
516
                        tabId: tab.id
517
                    });
518
                    API.browserAction.setTitle({
519
                        title: API.i18n.getMessage('browser_action_title_locked'),
520
                        tabId: tab.id
521
                    });
522
                }
523
            });
524
        }
525
    }
526
527
    function updateTabsIcon() {
528
        API.tabs.query({}).then(function (tabs) {
529
            for (var t = 0; t < tabs.length; t++) {
530
                var tab = tabs[t];
531
                createIconForTab(tab);
532
            }
533
        });
534
    }
535
536
537
    API.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) {
538
        if (master_password) {
539
            createIconForTab(tab);
540
        } else {
541
            displayLogoutIcons();
542
        }
543
    });
544
545
    API.tabs.onActivated.addListener(function () {
546
        API.tabs.query({active: true, currentWindow: true}).then(function (tabs) {
547
            if (master_password) {
548
                createIconForTab(tabs[0]);
549
            } else {
550
                displayLogoutIcons();
551
            }
552
        });
553
    });
554
555
    displayLogoutIcons();
556
557
    storage.get('master_password').then(function (password) {
558
        if (password) {
559
            master_password = password;
560
            API.api.browserAction.setBadgeBackgroundColor({
561
                color: defaultColor
562
            });
563
        }
564
        getSettings();
565
    }).error(function (error) {
566
        if (error === "Data not found") {
567
            getSettings();
568
        }
569
    });
570
    return _window;
571
}());
572
573